/**
 * Copyright 2006 Van de Wijgerd B.V. All rights reserved.
 */
package com.googlecode.friendfeed;

import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.cookie.CookiePolicy;

import java.io.IOException;

import net.sf.json.JSONObject;
import net.sf.json.JSONException;

public class FriendFeedApiImpl implements FriendFeedApi {
    private final AuthScope AUTH_SCOPE = new AuthScope("friendfeed.com", 80, AuthScope.ANY_REALM);
    private String apiUrl = DEFAULT_API_URL;

    private UsernamePasswordCredentials credentials = null;

    private HttpConnectionManager manager = null;

    public FriendFeedApiImpl(HttpConnectionManager manager) {
        this.manager = manager;
    }

    public FriendFeedApiImpl(HttpConnectionManager manager,String nickName, String remoteKey) {
        this(manager);
        this.credentials = new UsernamePasswordCredentials(nickName, remoteKey);
    }

    public void setManager(HttpConnectionManager manager) {
        this.manager = manager;
    }

    public Entries fetchPublicFeed() throws ApiException {
        GetMethod method = new GetMethod(apiUrl+"/feed/public");
        String json = execute(method);
        Entries entries = null;
        try {
            JSONObject jsonObject = JSONObject.fromObject(json);
            entries = (Entries) JSONObject.toBean(jsonObject,Entries.class,Entries.getClassMap());
        } catch (JSONException e) {
            throw new ApiException(json,e);
        }
        entries.setJson(json);
        return entries;
    }

    public Entries fetchUserFeed() throws ApiException {
        if(!hasCredentials()) {
            throw new ApiException("Authentication Required");
        }
        GetMethod method = new GetMethod(apiUrl+"/feed/user/"+credentials.getUserName());
        String json = execute(method);
        JSONObject jsonObject = JSONObject.fromObject(json);
        Entries entries = (Entries) JSONObject.toBean(jsonObject,Entries.class,Entries.getClassMap());
        entries.setJson(json);
        return entries;
    }

    public Entries fetchUserCommentedFeed() throws ApiException {
        if(!hasCredentials()) {
            throw new ApiException("Authentication Required");
        }
        GetMethod method = new GetMethod(apiUrl+"/feed/user/"+credentials.getUserName()+"/comments");
        String json = execute(method);
        JSONObject jsonObject = JSONObject.fromObject(json);
        Entries entries = (Entries) JSONObject.toBean(jsonObject,Entries.class,Entries.getClassMap());
        entries.setJson(json);
        return entries;
    }

    public Entries fetchUserLikedFeed() throws ApiException {
        if(!hasCredentials()) {
            throw new ApiException("Authentication Required");
        }
        GetMethod method = new GetMethod(apiUrl+"/feed/user/"+credentials.getUserName()+"/likes");
        String json = execute(method);
        JSONObject jsonObject = JSONObject.fromObject(json);
        Entries entries = (Entries) JSONObject.toBean(jsonObject,Entries.class,Entries.getClassMap());
        entries.setJson(json);
        return entries;
    }

    public Entries fetchUserDiscussedFeed() throws ApiException {
        if(!hasCredentials()) {
            throw new ApiException("Authentication Required");
        }
        GetMethod method = new GetMethod(apiUrl+"/feed/user/"+credentials.getUserName()+"/discussion");
        String json = execute(method);
        JSONObject jsonObject = JSONObject.fromObject(json);
        Entries entries = (Entries) JSONObject.toBean(jsonObject,Entries.class,Entries.getClassMap());
        entries.setJson(json);
        return entries;
    }

    public Entries fetchRoomFeed(String roomName) throws ApiException {
        if(!hasCredentials()) {
            throw new ApiException("Authentication Required");
        }
        GetMethod method = new GetMethod(apiUrl+"/feed/room/"+roomName);
        String json = execute(method);
        JSONObject jsonObject = JSONObject.fromObject(json);
        Entries entries = (Entries) JSONObject.toBean(jsonObject,Entries.class,Entries.getClassMap());
        entries.setJson(json);
        return entries;
    }

    public Profile getProfile() throws ApiException {
        if(!hasCredentials()) {
            throw new ApiException("Authentication Required");
        }
        GetMethod method = new GetMethod(apiUrl+"/user/"+credentials.getUserName()+"/profile");
        String json = execute(method);
        JSONObject jsonObject = JSONObject.fromObject(json);
        Profile profile = (Profile) JSONObject.toBean(jsonObject,Profile.class,Profile.getClassMap());
        profile.setJson(json);
        return profile;
    }

    public boolean hasCredentials() {
        return credentials != null;
    }

    private String execute(HttpMethod method) throws ApiException {
        method.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
        HttpClient httpClient = new HttpClient(manager);
        if (hasCredentials()) {
            httpClient.getState().setCredentials(AUTH_SCOPE, credentials);
            httpClient.getParams().setAuthenticationPreemptive(true);
        } else {
            httpClient.getParams().setAuthenticationPreemptive(false);
        }
        try {
            int statusCode = httpClient.executeMethod(method);
            if (statusCode != HttpStatus.SC_OK) {
                String error = String.format("Expected 200 OK. Received %d %s", statusCode, HttpStatus.getStatusText(statusCode));
                throw new ApiException(error);
            }
            String responseBody = method.getResponseBodyAsString();
            if (responseBody == null) {
                throw new ApiException("Expected response body, got null");
            }
            return responseBody;
        } catch (HttpException e) {
            throw new ApiException(e);
        } catch (IOException e) {
            throw new ApiException(e);
        } finally {
            method.releaseConnection();
        }
    }
}
